Post-Helene Impacts in Greater Asheville

Preliminary Findings

Published

June 1, 2025

This document presents an initial analysis of publicly available data sources that may shed light on how the post-hurricane recovery has unfolded in and around Asheville, North Carolina. Hurricane Helene devastated western North Carolina in late September of 2024 and the recovery efforts are still underway constrained in part by the politicization of funding at the state and the federal levels.



Click the down arrow to uncollapse the map above.



Although information like the building safety assessments dashboard linked above are available, privacy considerations prevent them being mapped with precision and they are often aggregated to the Census block or Census block group level. Before diving into the property sales and permitting data, let’s take a look at some of the information provided by FEMA.


FEMA Public Assistance

We can also look at datasets provided by FEMA related to “assistance made available to eligible state, local and tribal governments, and certain private nonprofit organizations as part of presidentially declared disasters” which began collection in 1998.

Applicant Number of Projects Federal $ Obligated County
Asheville Botanical Garden, Inc. (formerly: University Botanical Gardens at Asheville, Inc) 1 119,158.09 Buncombe County
Asheville Christian Academy 3 969,170.22 Buncombe County
Asheville City Schools 1 65,085.74 Buncombe County
Asheville, City of 3 10,353,343.23 Buncombe County
Asheville-Buncombe Community Christian Ministry, Inc. 1 202,500.00 Buncombe County
BUNCOMBE COUNTY RESCUE SQUAD 1 26,315.67 Buncombe County
Biltmore Forest, Town of 9 6,251,861.29 Buncombe County
Black Mountain, Town of 2 903,699.29 Buncombe County
Buncombe County 2 34,217,545.44 Buncombe County
Buncombe County Board of Education 1 4,291.20 Buncombe County
Christ School 3 361,229.61 Buncombe County
Deerfield Episcopal Retirement Community, Inc 1 105,500.00 Buncombe County
ENKA-CANDLER FIRE & RESCUE DEPT. INC. 2 148,349.53 Buncombe County
Evergreen Community Charter School 2 4,777.20 Buncombe County
First Christian Church of Black Mountain, North Carolina 2 10,776.44 Buncombe County
Francine Delany New School for Children 1 6,800.00 Buncombe County
Garren Creek Volunteer Fire Department 1 7,820.68 Buncombe County
Greater Asheville Regional Airport Authority 4 243,533.89 Buncombe County
Greater Works Church of God in Christ 1 15,570.00 Buncombe County
Invest Collegiate Imagine, Inc. 2 7,416.33 Buncombe County
Metropolitan Sewerage District of Buncombe County, North Carolina 5 709,186.48 Buncombe County
Montreat College 2 90,893.28 Buncombe County
Montreat, Town of 1 24,800.77 Buncombe County
Mountain City Public Montessori 2 9,820.78 Buncombe County
Mountain Housing Opportunities, Inc. 2 126,364.98 Buncombe County
Odyssey: A Community of Integral Learning, Inc. 2 42,720.62 Buncombe County
Reems Creek Valley Fire Department Inc. 1 294,078.92 Buncombe County
Reynolds Volunteer Fire Department INC. 1 82,780.19 Buncombe County
Riceville Volunteer Fire Department 1 132,796.90 Buncombe County
Swannanoa Fire Department & Rescue Squad, Inc. 1 101,371.95 Buncombe County
The Franklin School of Innovation, Inc. 1 22,882.53 Buncombe County
The Learning Community Inc. 5 156,990.05 Buncombe County
The North Carolina Arboretum 2 61,212.26 Buncombe County
The University of North Carolina - Asheville 5 2,118,803.40 Buncombe County
United Way of Asheville and Buncombe County 2 16,857.23 Buncombe County
Warren Wilson College 2 3,321,150.00 Buncombe County
Weaverville, Town of 3 1,914,499.92 Buncombe County
Western North Carolina Community Health Services 1 22,917.94 Buncombe County
Woodfin, Town of 2 906,724.05 Buncombe County
YMCA of Western North Carolina, Inc. 1 71,100.00 Buncombe County
CHIMNEY ROCK VILLAGE 6 1,235,270.10 Rutherford County
Cherry Mountain Volunteer Fire Department 2 38,664.43 Rutherford County
Forest City, Town of 5 1,513,780.49 Rutherford County
Isothermal Community College 1 4,695.00 Rutherford County
Lake Lure, Town of 6 3,188,608.07 Rutherford County
Rutherford County 1 7,643,529.69 Rutherford County
Rutherford County Health Department 2 78,003.60 Rutherford County
Rutherfordton, Town of 5 320,298.71 Rutherford County
Shiloh-Danieltown-Oakland Volunteer Fire Department Inc 1 18,575.26 Rutherford County
Shingle Hollow Volunteer Fire Department, Inc. 1 79,990.14 Rutherford County
Spindale, Town of 1 126,244.42 Rutherford County
Town of Bostic 2 29,284.97 Rutherford County
Union Mills Volunteer Fire Department Inc 2 142,739.00 Rutherford County

As shown above, just over $64 million in Public Assistance funds were obligated in Buncombe County over a total of 85 projects while in Rutherford County about $14.4 million was obligated across 35 projects. The table above shows individual applicants, the total Public Assistance funds obligated, and the number of projects for the two counties. There appears to be a wide range of organizations among the applicants from local government, to churches and educational institutions.


FEMA Individual Assistance

We can also look at FEMA Housing Assistance program datasets for both owners and renters living in areas with a valid disaster declaration (i.e., 4827 for Tropical Storm Helene in North Carolina). It should be noted that these data “self-reported and subject to human error”.

damage <- ha_owners %>% dplyr::filter(county == "Buncombe (County)" | county == "Rutherford (County)") %>%
  group_by(county) %>%
  summarise(
    total_damage = sum(totalDamage),
    total_inspections = sum(totalInspected), 
    total_repair_replace = sum(repairReplaceAmount)
  ) %>%
ggplot() +
    geom_bar(aes(y = total_damage, x = county), fill = "#E57200", alpha = 0.75, 
             stat = "identity") +
    scale_y_continuous(labels = scales::dollar_format()) +
    labs(x = " ", y = "Total Damage ($)", title = "FEMA Housing Assistance Funding for Owners", subtitle = "Hurricane Helene Declaration") +
  ylim(c(0, 85000000)) +
    theme_minimal() +
    theme(plot.subtitle = element_text(hjust = 0.5), 
        plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) + 
    geom_text(aes(label = total_inspections, x = county, y = total_damage), vjust = 1.8, color = "white", size = 7)
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
repair_replace <- ha_owners %>% dplyr::filter(county == "Buncombe (County)" | county == "Rutherford (County)") %>%
  group_by(county) %>%
  summarise(
    total_damage = sum(totalDamage),
    total_inspections = sum(totalInspected), 
    total_repair_replace = sum(repairReplaceAmount)
  ) %>%
ggplot() +
    geom_bar(aes(y = total_repair_replace, x = county), fill = "#232D4B", alpha = 0.75, 
             stat = "identity") +
    scale_y_continuous(labels = scales::dollar_format()) +
    labs(x = " ", y = "Total Approved for Repair/Replace ($)", title = "FEMA Housing Assistance Funding for Owners", subtitle = "Hurricane Helene Declaration", caption = "Bar labels are the total number of inspections completed by FEMA.") +
    ylim(c(0, 85000000)) +
    theme_minimal() +
    theme(plot.subtitle = element_text(hjust = 0.5), 
        plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) + 
    geom_text(aes(label = total_inspections, x = county, y = total_repair_replace), vjust = 1.8, color = "white", size = 7)
Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
ggarrange(damage, repair_replace, nrow = 1, ncol = 2)

ggsave("Housing Assistance for Owners - Damage.png", units = "in", 
       width = 16, height = 8, bg = "white")

The figure above shows Housing Assistance for owners was far greater in Buncombe County than in Rutherford County both in terms of total damage recorded by FEMA at the time of the latest inspection—which includes FEMA Verified Loss (FVL) for real property (e.g., home damage) and personal property (e.g., appliance, rooms, essential tools, and other miscellaneous items). Total damage in Rutherford County was 12% of that in Buncombe County, the total Repair and/or Replacement approved dollars was 11.5% of that in Buncombe County, and completed inspections were 13.9% of those in Buncombe County.


We can also look at Housing Assistance for renters but important differences exist relative to owners—for example renters do not receive a full home inspection as they are only eligible for the items that they own.

rental_assistance <- ha_renters %>% dplyr::filter(county == "Buncombe (County)" | county == "Rutherford (County)") %>%
  group_by(county) %>%
  summarise(
    total_applicants_approved = sum(approvedForFemaAssistance),
    total_applicants_registered = sum(validRegistrations), 
    total_rental_assist = sum(rentalAmount), 
    pc_rental_assist = total_rental_assist / total_applicants_approved
  ) %>%
ggplot() +
    geom_bar(aes(y = total_rental_assist, x = county, fill = county), alpha = 0.75, 
             stat = "identity") +
    scale_y_continuous(labels = scales::dollar_format()) +
    scale_fill_manual(name = "", values = c("#232D4B", "#E57200"), labels = c("Buncombe", "Rutherford")) +
      scale_x_discrete(labels = c("Buncombe", "Rutherford")) +
    labs(x = " ", y = "Total  Rental Assistance Approved ($)", title = "FEMA Housing Assistance Funding for Renters", subtitle = "Hurricane Helene Declaration") +
    theme_minimal() +
    theme(plot.subtitle = element_text(hjust = 0.5), 
        plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) +
  guides(fill = "none")


ra_applicants <- ha_renters %>% dplyr::filter(county == "Buncombe (County)" | county == "Rutherford (County)") %>%
  group_by(county) %>%
  summarise(
    total_applicants_approved = sum(approvedForFemaAssistance),
    total_applicants_registered = sum(validRegistrations)
  ) %>%
  pivot_longer(cols = starts_with("total"), names_to = "variable", values_to = "value") %>%
ggplot() +
    geom_bar(aes(x = variable, fill = as_factor(county), y = value), alpha = 0.75, 
             stat = "identity", position = "dodge") +
    scale_y_continuous(labels = scales::comma_format()) +
    scale_x_discrete(labels = c("Applicants Approved \n for Assistance", "Registered Applicants")) +
    scale_fill_manual(name = "", values = c("#232D4B", "#E57200"), labels = c("Buncombe", "Rutherford")) +
    labs(x = "", y = "Total Registered vs Applicants Approved", title = "FEMA Housing Assistance Funding for Renters", subtitle = "Hurricane Helene Declaration", caption = "Buncombe = 61% approved, Rutherford = 57% approved") +
    theme_minimal() +
    theme(plot.subtitle = element_text(hjust = 0.5), 
        plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) + 
    geom_text(aes(label = value, x = variable, y = value), vjust = 1.8, color = "white", size = 3, 
              position = position_dodge2(width = .9))


ggarrange(ra_applicants, rental_assistance, nrow = 1, ncol = 2)

ggsave("Housing Assistance for Renters - Assistance.png", units = "in", 
       width = 16, height = 8, bg = "white")

The approval rate for Housing Assistance among registered renters was higher in Buncombe (61%) than in Rutherford County (57%) and the total rental assistance approved per capita—based on the number of approved renter applicants—was $98.70 and $47.20 respectively. The per capita figure seems low until we consider that in Buncombe County 23,894 of the 24,222 (98.6%) approved renter applicants received “a financial grant from FEMA that fell between $1 and $10,000” while in Rutherford County 2,805 of 2,815 (99.6%) approved renter applicants received this lowest tier of Housing Assistance.


Buncombe County Sales and Permits

We can begin to push beyond the limitations of the FEMA data (e.g., aggregated to county, locality, or zip code) by exploring data on property sales that occurred since October 1, 2024 at the parcel level.



The webpage above is interactive.



The figures and maps below show the volume and location of properties sold in Buncombe county since October 1, 2024.

tmap_mode("view")

tm_shape(buncombe_co_parcels_sales_4326 %>% filter(is.na(MktLessSold) == FALSE, 
                                                   MktLessSold > 0, 
                                                   MktValue > 0, 
                                                   as_date(SellDat) > as_date("2024-10-01"), 
                                                  QualfdS == "Y", 
                                                   SllngPr > 1000), name = "Parcels") + 
  tm_polygons(fill = "MktLessSold",
              fill.scale = tm_scale_intervals(values = "brewer.greens", style = "fisher"),
              fill.legend = tm_legend(title = "Assessed Minus Sale Price", title.size = 0.8),
  popup.vars = c("Owner name: " = "Owner", "Subdivision name: " = "SubName",
                 "Total market value: " = "MktValue", "Sale price: " = "SllngPr", "Sale date: " = "SellDat")) +
tm_shape(localities_0, name = "Jurisdictions") + 
  tm_borders(col = "Dscrptn", lwd = 2, id = "Dscrptn",
             col.legend = tm_legend(title = "Localities")) +
tm_shape(buncombe_flood_extent, name = "Flood Extent") + 
  tm_polygons("dodgerblue", fill_alpha = 0.8) +
tm_shape(buncombe_flooded_structures, name = "Parcels with \n Flooded Structures") + 
  tm_polygons("yellow3", fill_alpha = 0.2) +
tm_title("Properties Sold Since Helene, Less Than Market Value", frame = FALSE) +
tm_basemap(c("Esri.WorldGrayCanvas", "CartoDB.Positron", "Esri.WorldTopoMap"))
buncombe_co_parcels_sales_4326 %>%
  st_drop_geometry() %>%
  dplyr::filter(SellDat > "2024-10-01", 
                QualfdS == "Y") %>%
  mutate(the_month = lubridate::month(SellDat)) %>%
  group_by(the_month) %>%
  summarise(avg_price = mean(MktValue),
            tot_sales = n()) %>%
  distinct( .keep_all = TRUE) %>%
  mutate(label_month = case_when(the_month == 01 ~ 'Jan',
                                 the_month == 02 ~ 'Feb',
                                 the_month == 03 ~ 'Mar',
                                 the_month == 04 ~ 'Apr',
                                 the_month == 05 ~ 'May',
                                 the_month == 06 ~ 'Jun',
                                 the_month == 07 ~ 'Jul',
                                 the_month == 08 ~ 'Aug',
                                 the_month == 09 ~ 'Sep',
                                 the_month == 10 ~ 'Oct',
                                 the_month == 11 ~ 'Nov',
                                 the_month == 12 ~ 'Dec'),
          sort_month = case_when(the_month == 01 ~ 4,
                                 the_month == 02 ~ 5,
                                 the_month == 03 ~ 6,
                                 the_month == 04 ~ 7,
                                 the_month == 05 ~ 8,
                                 the_month == 06 ~ 9,
                                 the_month == 07 ~ 10,
                                 the_month == 08 ~ 11,
                                 the_month == 09 ~ 12,
                                 the_month == 10 ~ 1,
                                 the_month == 11 ~ 2,
                                 the_month == 12 ~ 3)) %>%
  ggplot() + 
  geom_bar(aes(y = avg_price, x = reorder(label_month, sort_month)), fill = "dodgerblue", stat = "identity") +
#  scale_color_brewer(palette = "Set1", name = "Month") +
  labs(x = " ", y = "Mean Sale Price ($)", title = "Buncombe County Average Sale Price, Oct. 2024 to Present") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) +
  geom_text(aes(label = tot_sales, x = reorder(label_month, sort_month), y = avg_price), vjust = 1.8, color = "white", size = 7)

Note the divergence between number of sales and mean sale price in the bar chart above.

There were 180 “arms length” sales recorded in Buncombe county and its localities between October 1, 2024 and late April of 2025 and of these, only 25 were for less than the assessed value. Of those 25 sales, 13 are recorded as vacant lots.

Perhaps surprisingly, there does not appear to be much of a correlation between flood inundation—as reflected in this particular data layer—and sales at a price below assessed value. There are a few examples of this phenomenon but they tended to occur soon after the hurricane.


With that said, where are new building permits being issued in Buncombe County?

tmap_mode(mode = "view") 

tm_shape(localities_0, name = "Jurisdictions") + 
  tm_borders(col = "Dscrptn", lwd = 2, id = "Dscrptn",
             col.legend = tm_legend(title = "Localities")) +
tm_shape(buncombe_flood_extent, name = "Flood Extent") + 
  tm_polygons("dodgerblue", fill_alpha = 0.8) +
tm_shape(buncombe_flooded_structures, name = "Parcels with \n Flooded Structures") + 
  tm_polygons("yellow3", fill_alpha = 0.2) +
  
tm_shape(shp = buncombe_co_parcels_permits %>% st_transform(st_crs(bgs_buncombe)) %>% dplyr::filter(as.Date(buncombe_co_parcels_permits$IssuDat, "%m/%d/%Y") > as.Date("10/01/2024", "%m/%d/%Y")), 
           name = "Permits", crs = "auto") +
  tm_dots(fill="TypofUs", fill_alpha = 0.5, size = 0.75, 
              popup.vars = c("Total Market Value: " = "TtlMrkV", "City: " = "CityNam", "Permit #: " = "Permit.",
                             "Use: " = "TypofUs", "Work: " = "TypofWr", "Total Cost: " = "TotlCst"), 
              tm_legend(title = "Permits", show = TRUE),
              fill.scale = tm_scale_categorical(values = "magma")
  ) +
  tm_view(set_view = c(-82.53135846074531, 35.6052529877064,  11)) + 
  tm_basemap(c("CartoDB.Positron", "Esri.WorldTopoMap", "OpenStreetMap.Mapnik", "Esri.WorldImagery")) +
  tm_title("Permits Issued After Hurricane Helene", position = tm_pos_in("right", "TOP"), frame = FALSE) +
  tm_layout(frame = FALSE) 
tmap_mode(mode = "view") 

tm_shape(shp = bgs_buncombe, name = "Census Block Groups", crs = "auto") +
  tm_borders(col = "dodgerblue", lwd = 0.8, fill_alpha = 0.4) +
  tm_shape(shp = bgs_to_plot %>% st_transform(st_crs(bgs_buncombe)), 
           name = "Repetitive Losses", crs = "auto") +
  tm_polygons(fill="totalLosses", fill_alpha = 0.8, 
              popup.vars = c("Total Losses: " = "totalLosses", "Flood Zone: " = "floodZone", "City: " = "reportedCity", "Most Recent \n Loss: " = "mostRecentDateofLoss"), 
              tm_legend(title = "Losses"), 
              fill.scale = tm_scale(values = "viridis")
  ) +
  tm_shape(shp = buncombe_co_parcels_permits %>% st_transform(st_crs(bgs_buncombe)) %>% filter(TypofUs == "Single Family Site Built"), 
           name = "Single Family Permits", crs = "auto") +
  tm_dots(fill="purple", fill_alpha = 0.5, size = 0.75, 
              popup.vars = c("Total Market Value: " = "TtlMrkV", "City: " = "CityNam", "Permit #: " = "Permit.",
                             "Use: " = "TypofUs", "Work: " = "TypofWr", "Total Cost: " = "TotlCst"), 
              tm_legend(title = "Single Family Permits", show = TRUE)
  ) +
tm_shape(shp = buncombe_co_parcels_permits %>% st_transform(st_crs(bgs_buncombe)) %>% filter(TypofUs == "Townhouse"), 
           name = "Townhouse Permits", crs = "auto") +
  tm_dots(fill="pink", fill_alpha = 0.5, size = 0.75, 
              popup.vars = c("Total Market Value: " = "TtlMrkV", "City: " = "CityNam", "Permit #: " = "Permit.",
                             "Use: " = "TypofUs", "Work: " = "TypofWr", "Total Cost: " = "TotlCst"), 
              tm_legend(title = "Townhouse Permits", show = TRUE)
  ) +
tm_shape(shp = buncombe_co_parcels_permits %>% st_transform(st_crs(bgs_buncombe)) %>% filter(TypofUs == "HUD Labeled Manufactured Home"), 
           name = "Manufactured Home Permits", crs = "auto") +
  tm_dots(fill="goldenrod", fill_alpha = 0.3, size = 0.75, 
              popup.vars = c("Total Market Value: " = "TtlMrkV", "City: " = "CityNam", "Permit #: " = "Permit.",
                             "Use: " = "TypofUs", "Work: " = "TypofWr", "Total Cost: " = "TotlCst"), 
              tm_legend(title = "Manufactured Home Permits", show = TRUE)
  ) +
  tm_view(set_view = c(-82.53135846074531, 35.6052529877064,  11)) + 
  tm_basemap(c("CartoDB.Positron", "Esri.WorldTopoMap", "OpenStreetMap.Mapnik", "Esri.WorldImagery")) +
  tm_title("Repetitive Loss Properties and Permits Issued Post Helene", position = tm_pos_in("right", "TOP"), frame = FALSE) +
  tm_layout(frame = FALSE) 

From the first interactive map above, we see quite a bit of permitting activity within the estimated flood extent—particularly in the Swannanoa area with residential alterations and modular/manufactured housing as the most frequent permit types.

The second interactive map above shows specific types of permits as separate layers that can be toggled on and off alongside repetitive flood loss properties aggregated to the Census block group by FEMA.

Clearly these is a lot of growth and development happening in this area of North Carolina which likely obscures land use and housing metrics as indicators of hurricane recovery.


Rutherford County Sales and Permits

Rutherford County is adjacent to Buncombe County and has roughly one-quarter of Buncombe’s population. We can begin to look at property sale transactions and permitting there as well.



The webpage above is interactive.



The figures and maps below show the volume and location of properties sold in Rutherford county since October 1, 2024.

tmap_mode("view")

tm_shape(st_make_valid(rutherford_co_parcels_possible_sales_4326) %>% filter(is.na(MktLessSold) == FALSE,
                                                   MktLessSold > 1000,
                                                   MktValue > 0,
                                                   PRICE > 1000), name = "Parcels") +
  tm_polygons(fill = "MktLessSold",
              fill.scale = tm_scale_intervals(values = "brewer.greens", style = "fisher"),
              fill.legend = tm_legend(title = "Assessed Minus Sale Price", title.size = 0.8),
  popup.vars = c("Owner name: " = "Property_O", "Land use: " = "Land_Class.x",
                 "Total market value: " = "MktValue", "Sale price: " = "PRICE", "Sale date: " = "SALE_DATE")) +
tm_shape(localities_1, name = "Jurisdictions") +
  tm_borders(col = "MUNICIPALI", lwd = 2, id = "MUNICIPALI",
             col.legend = tm_legend(title = "Localities")) +
tm_title("Properties Sold Since Helene, Less Than Market Value", frame = FALSE) +
tm_basemap(c("Esri.WorldGrayCanvas", "CartoDB.Positron", "Esri.WorldTopoMap"))
to_plot <- rutherford_co_parcels_possible_sales_4326 %>%
  st_drop_geometry() %>%
  dplyr::filter(SALE_DATE > "2024-10-01") %>%
  mutate(the_month = lubridate::month(SALE_DATE)) %>%
  group_by(the_month) %>%
  summarise(avg_price = mean(MktValue),
            tot_sales = n()) %>%
  distinct( .keep_all = TRUE) %>%
  mutate(label_month = case_when(the_month == 01 ~ 'Jan',
                                 the_month == 02 ~ 'Feb',
                                 the_month == 03 ~ 'Mar',
                                 the_month == 04 ~ 'Apr',
                                 the_month == 05 ~ 'May',
                                 the_month == 06 ~ 'Jun',
                                 the_month == 07 ~ 'Jul',
                                 the_month == 08 ~ 'Aug',
                                 the_month == 09 ~ 'Sep',
                                 the_month == 10 ~ 'Oct',
                                 the_month == 11 ~ 'Nov',
                                 the_month == 12 ~ 'Dec'),
          sort_month = case_when(the_month == 01 ~ 4,
                                 the_month == 02 ~ 5,
                                 the_month == 03 ~ 6,
                                 the_month == 04 ~ 7,
                                 the_month == 05 ~ 8,
                                 the_month == 06 ~ 9,
                                 the_month == 07 ~ 10,
                                 the_month == 08 ~ 11,
                                 the_month == 09 ~ 12,
                                 the_month == 10 ~ 1,
                                 the_month == 11 ~ 2,
                                 the_month == 12 ~ 3)) 

ggplot() + 
  geom_bar(data = to_plot, aes(y = avg_price, x = reorder(label_month, sort_month)), fill = "dodgerblue", stat = "identity") +
#  scale_color_brewer(palette = "Set1", name = "Month") +
  labs(x = " ", y = "Mean Sale Price ($)", title = "Rutherford County Average Sale Price, Oct. 2024 to May 2025") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) +
  geom_text(data = to_plot, aes(label = tot_sales, x = reorder(label_month, sort_month), y = avg_price), vjust = 1.8, color = "white", size = 7)

to_plot <- rutherford_co_parcels_possible_sales_4326 %>%
  st_drop_geometry() %>%
    filter(MktValue > 0, 
          PRICE > 1000, 
          Land_Class.x != "Vacant with Misc Imp", 
          Land_Class.x == "SINGLE FAMILY RES")%>%
  group_by(Land_Class.x, City) %>%
  dplyr::select(Land_Class.x, PRICE, MktValue, Heated_Are, Acreage, City) %>%
  # `colnames<-`(c("Land Use", "Sale Price", "Assessed Value", "Square Footage",
  #                "Lot Acres", "City")) %>%
  summarise(numSales = n(), 
            meanSold = mean(PRICE),
            meanAssessed = mean(MktValue), 
            diffSoldAssessed = meanSold - meanAssessed
          )

ggplot() + 
  geom_bar(data = to_plot, aes(y = meanSold, x = str_to_title(City), fill = str_to_title(City)), alpha = 0.75, stat = "identity") + 
  scale_y_continuous(labels = scales::dollar_format()) +
  scale_fill_viridis_d(name = "Jurisdiction") +
    labs(x = "", y = "Mean Sales Price ($)", title = "Single Family Residential Sales, Oct. 2024 to May 2025", subtitle = "Rutherford County, NC", caption = "Bar labels are the mean assessed value") +
    theme_minimal() +
    theme(plot.subtitle = element_text(hjust = 0.5), 
        plot.title = element_text(hjust = 0.5, size = 16), 
        axis.text.x = element_text(size = 12),
        text = element_text(size = 18)) + 
    geom_text(data = to_plot, aes(label = dollar(round(meanAssessed, 2)), x = str_to_title(City), y = meanAssessed), vjust = 1.8, color = "white", size = 5)

There is a town of roughly 350 residents called Ruth located to the north of the county seat Rutherfordton. The difference between assessed vale and sale price for single family residential properties sold since October 1, 2024 was the smallest in Ruth, but for context only one sale was recorded there. The gap between assessed value and sale price was greatest in Lake Lure (18 sales) and Rutherford County (118 sales).

Of the 310 sales recorded in Rutherford County (and its localities) between October 1, 2024 and May 1, 2025 only 42 sold for less than the assessed value. The vast majority of non-vacant properties sold in Rutherford County since Hurricane Helene has sold for more than the assessed value.


What is the story with building permits?

rutherford_co_permits_0 <- read_csv("./Rutherford_County_Permits/Rutherford County Permits.csv")

rutherford_co_permits <- st_as_sf(rutherford_co_permits_0, coords = c("POINT_X", "POINT_Y"), 
                                       crs = 4326)


tmap_mode(mode = "view") 

tm_shape(localities_1, name = "Jurisdictions") +
  tm_borders(col = "MUNICIPALI", lwd = 2, id = "MUNICIPALI",
             col.legend = tm_legend(title = "Localities")) +
tm_shape(shp = rutherford_co_permits %>% dplyr::filter(USER_Type == "Building (Commercial) Alteration, Remodel, Repair" | USER_Type == "Building (Residential)  Alteration, Remodel, Repair"), name = "Repair Permits", crs = "auto") +
  tm_dots(fill="purple", fill_alpha = 0.5, size = 0.75,
              popup.vars = c("Permit Issued: " = "USER_Issued_Date", "Use: " = "USER_Type", "Status: " = "USER_Status", "Address: " = "Match_addr", "Description: " = "USER_Description"),
              tm_legend(title = "Repair Permits", show = TRUE)
  ) +
 tm_shape(shp = rutherford_co_permits %>% dplyr::filter(USER_Type == "Building (Commercial) New Construction" | USER_Type == 'Building (Residential New Home) ""Single Family or Duplex""' | USER_Type == "Building (Residential) Mobile Home"),
                name = "New Structure Permits", crs = "auto") +
  tm_dots(fill="pink", fill_alpha = 0.75, size = 0.75,
              popup.vars = c("Permit Issued: " = "USER_Issued_Date", "Use: " = "USER_Type", "Status: " = "USER_Status", "Address: " = "Match_addr", "Description: " = "USER_Description"),
              tm_legend(title = "New Structure Permits", show = TRUE)
  ) +
tm_view(set_view = c(-81.89957980836765, 35.39858178546087,  11)) +
tm_basemap(c("CartoDB.Positron", "Esri.WorldTopoMap", "OpenStreetMap.Mapnik", "Esri.WorldImagery")) +
tm_title("Permits Issued in Rutherford County, Oct. 2024 to Present", position = tm_pos_in("right", "TOP"), frame = FALSE) +
tm_layout(frame = FALSE)

This is just an initial pass through the data, but there does not seem to be much evidence of many properties being sold at a loss in the wake of the hurricane. The next step should be to refine hypotheses and to think about ways to tweak and/or expand this kind of analysis so that it better supports the research questions.

One idea would be to take a deeper dive into the types, volume, and location of permits in the counties and perhaps to compare those trends with what was happening in the 6-8 months before Helene.